﻿/*
 * CitBitmap
 * 
 * initial implementation : 3.12.2008
 *
 * Copyright (c) Adam Sida, asida@centrum.cz.
 * All rights reserved.
 * 
 * for license see Petra.Gis.License.txt
 */
using System;
using System.IO;
using System.Collections.Generic;
using System.Windows;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Runtime.InteropServices;

namespace Petra.Gis.ImagingServices.Cit
{

  /// <summary>
  /// represents cit BitmapSource with specific format, metadata and pixel manipulation
  /// cit is always bilevel image with CCITT G4 2d compression
  /// </summary>
  public class CitBitmap : BitmapSource
  {

    protected BinaryReader _citReader;
    protected bool _decodeAs8bpp;
    protected Header1 h1;
    BitmapCacheOption _cacheOption;


    protected CitBitmap(Stream citStream, BitmapCreateOptions createOptions, BitmapCacheOption cacheOption)
    {

      _citReader = new BinaryReader(citStream);

      if (createOptions == BitmapCreateOptions.PreservePixelFormat)
        _decodeAs8bpp = false;
      else
        _decodeAs8bpp = true;

      //_decodeAs8bpp = decodeAs8bpp;
      _cacheOption = cacheOption;

      _citReader.BaseStream.Seek(0, SeekOrigin.Begin);

      //read magic numbers...
      readHeaderMagicNumber(_citReader);

      // read standard headers
      // anyway only h1 is filled for cit
      h1 = readHeader1(_citReader);
      Header2 h2 = readHeader2(_citReader);

      //set dimensions
      _pixelWidth = (int)h1.PixelsPerLine;
      _pixelHeight = (int)h1.NumberOfLines;

      //set pixelFormat
      if (_decodeAs8bpp)
        _pixelFormat = PixelFormats.Indexed8;
      else
        _pixelFormat = PixelFormats.BlackWhite;


      //DependencyPropertyKey MtdtKey = DependencyProperty.RegisterReadOnly(
      //"TransformMatrix",
      //typeof(double[]),
      //typeof(ImageMetadata),
      //new PropertyMetadata(new double[16])
      //);

      //data.Metadata.SetValue(MtdtKey, h1.TransformationMatrix);
    }


    #region Freezable

    /// <summary>
    ///     Creates an instance of a CitBitmap.
    /// </summary>
    /// <returns>
    ///     The new instance.
    /// </returns>
    /// <remarks>
    ///     The default implementation uses reflection to create a new
    ///     instance of this type.  Derived classes may override this
    ///     method if they wish to provide a more performant
    ///     implementation, or if their type does not have a public
    ///     default constructor.
    /// </remarks>
    protected override Freezable CreateInstanceCore()
    {
      return (Freezable)Activator.CreateInstance(this.GetType());
    }

    /// <summary>
    ///     Copies data into a cloned instance.
    /// </summary>
    /// <param name="source">
    ///     The original instance to copy data from.
    /// </param>
    /// <remarks>
    ///     When Freezable is cloned, WPF will make deep clones of all
    ///     writable, locally-set properties including expressions. The
    ///     property's base value is copied -- not the current value. WPF
    ///     skips read only DPs.
    ///     
    ///     If you derive from this class and have additional non-DP state
    ///     that should be transfered to copies, you should override the
    ///     CopyCommon method.
    /// </remarks>
    protected sealed override void CloneCore(Freezable source)
    {
      base.CloneCore(source);

      CitBitmap customBitmapSource = (CitBitmap)source;
      CopyCore(customBitmapSource, /*useCurrentValue*/ false, /*willBeFrozen*/ false);
    }

    /// <summary>
    ///     Copies data into a cloned instance.
    /// </summary>
    /// <param name="source">
    ///     The original instance to copy data from.
    /// </param>
    /// <remarks>
    ///     When a Freezable's "current value" is cloned, WPF will make
    ///     deep clones of the "current values" of all writable,
    ///     locally-set properties. This has the effect of resolving
    ///     expressions to their values. WPF skips read only DPs.
    ///     
    ///     If you derive from this class and have additional non-DP state
    ///     that should be transfered to copies, you should override the
    ///     CopyCommon method.
    /// </remarks>
    protected sealed override void CloneCurrentValueCore(Freezable source)
    {
      base.CloneCurrentValueCore(source);

      CitBitmap customBitmapSource = (CitBitmap)source;
      CopyCore(customBitmapSource, /*useCurrentValue*/ true, /*willBeFrozen*/ false);
    }

    /// <summary>
    ///     Copies data into a cloned instance.
    /// </summary>
    /// <param name="source">
    ///     The original instance to copy data from.
    /// </param>
    /// <remarks>
    ///     Freezable.GetAsFrozen is semantically equivalent to
    ///     Freezable.Clone().Freeze(), except that you can avoid copying
    ///     any portions of the Freezable graph which are already frozen.
    ///     
    ///     If you derive from this class and have additional non-DP state
    ///     that should be transfered to copies, you should override the
    ///     CopyCommon method.
    /// </remarks>
    protected sealed override void GetAsFrozenCore(Freezable source)
    {
      base.GetAsFrozenCore(source);

      CitBitmap customBitmapSource = (CitBitmap)source;
      CopyCore(customBitmapSource, /*useCurrentValue*/ false, /*willBeFrozen*/ true);
    }

    /// <summary>
    ///     Copies data into a cloned instance.
    /// </summary>
    /// <param name="source">
    ///     The original instance to copy data from.
    /// </param>
    /// <remarks>
    ///     Freezable.GetCurrentValueAsFrozen is semantically equivalent to
    ///     Freezable.CloneCurrentValue().Freeze(), except that WPF will
    ///     avoid copying any portions of the Freezable graph which are
    ///     already frozen.
    ///     
    ///     If you derive from this class and have additional non-DP state
    ///     that should be transfered to copies, you should override the
    ///     CopyCommon method.
    /// </remarks>
    protected sealed override void GetCurrentValueAsFrozenCore(Freezable source)
    {
      base.GetCurrentValueAsFrozenCore(source);

      CitBitmap customBitmapSource = (CitBitmap)source;
      CopyCore(customBitmapSource, /*useCurrentValue*/ true, /*willBeFrozen*/ true);
    }

    /// <summary>
    ///     Copies data into a cloned instance.
    /// </summary>
    /// <param name="original">
    ///     The original instance to copy data from.
    /// </param>
    /// <param name="useCurrentValue">
    ///     Whether or not to copy the current value of expressions, or the
    ///     expressions themselves.
    /// </param>
    /// <param name="willBeFrozen">
    ///     Indicates whether or not the clone will be frozen.  If the
    ///     clone will be immediately frozen, there is no need to clone
    ///     data that is already frozen, you can just share the instance.
    /// </param>
    /// <remarks>
    ///     Override this method if you have additional non-DP state that
    ///     should be transfered to clones.
    /// </remarks>
    protected virtual void CopyCore(CitBitmap original, bool useCurrentValue, bool willBeFrozen)
    {
    }

    #endregion Freezable

    #region BitmapSource Properties

    /// <summary>
    ///     Horizontal DPI of the bitmap.
    /// </summary>
    /// <remarks>
    ///     Derived classes can override this to specify their own value.
    /// </remarks>
    public override double DpiX
    {
      get
      {
        return 96.0;
      }
    }

    /// <summary>
    ///     Vertical DPI of the bitmap.
    /// </summary>
    /// <remarks>
    ///     Derived classes can override this to specify their own value.
    /// </remarks>
    public override double DpiY
    {
      get
      {
        return (double)96.0;
      }
    }



    protected PixelFormat _pixelFormat;


    /// <summary>
    ///     Pixel format of the bitmap.
    /// </summary>
    /// <remarks>
    ///     Derived classes can override this to specify their own value.
    /// </remarks>
    public override PixelFormat Format
    {
      get
      {
        return _pixelFormat;
      }
    }



    protected int _pixelWidth;

    /// <summary>
    ///     Width of the bitmap contents.
    /// </summary>
    /// <remarks>
    ///     Derived classes can override this to specify their own value.
    /// </remarks>
    public override int PixelWidth
    {
      get
      {
        return _pixelWidth;
      }
    }



    protected int _pixelHeight;


    /// <summary>
    ///     Height of the bitmap contents.
    /// </summary>
    /// <remarks>
    ///     Derived classes can override this to specify their own value.
    /// </remarks>
    public override int PixelHeight
    {
      get
      {
        return _pixelHeight;
      }
    }

    /// <summary>
    ///     Palette of the bitmap.
    /// </summary>
    /// <remarks>
    ///     Derived classes can override this to specify their own value.
    /// </remarks>
    public override BitmapPalette Palette
    {
      get
      {
        if (_decodeAs8bpp)
        {
          List<Color> tmpColorList = new List<Color>();
          tmpColorList.Add(Color.FromArgb(0, 0, 0, 0));

          //put some gray colors between...
          for (int i = 1; i < 255; i++)
            tmpColorList.Add(Color.FromArgb(255, 128, 128, 128));

          tmpColorList.Add(Color.FromArgb(255, 255, 255, 255));

          return new BitmapPalette(tmpColorList);
        }
        else
          return null;
      }
    }

    #endregion BitmapSource Properties

    #region BitmapSource CopyPixels

    /// <summary>
    ///     Requests pixels from this BitmapSource.
    /// </summary>
    /// <param name="pixels">
    ///     The destination array of pixels.
    /// </param>
    /// <param name="stride">
    ///     The stride of the destination array.
    /// </param>
    /// <param name="offset">
    ///     The starting index within the destination array to copy to.
    /// </param>
    /// <remarks>
    ///     Derived classes must override CopyPixelsCommon to implement
    ///     custom logic.
    /// </remarks>
    public sealed override void CopyPixels(Array pixels, int stride, int offset)
    {
      Int32Rect sourceRect = new Int32Rect(0, 0, this.PixelWidth, this.PixelHeight);
      CopyPixels(sourceRect, pixels, stride, offset);
    }

    /// <summary>
    ///     Requests pixels from this BitmapSource.
    /// </summary>
    /// <param name="sourceRect">
    ///     The rectangle of pixels to copy.
    /// </param>
    /// <param name="pixels">
    ///     The destination array of pixels.
    /// </param>
    /// <param name="stride">
    ///     The stride of the destination array.
    /// </param>
    /// <param name="offset">
    ///     The starting index within the destination array to copy to.
    /// </param>
    /// <remarks>
    ///     Derived classes must override CopyPixelsCommon to implement
    ///     custom logic.
    /// </remarks>
    public sealed override void CopyPixels(Int32Rect sourceRect, Array pixels, int stride, int offset)
    {
      int elementSize;
      int bufferSize;
      Type elementType;
      ValidateArrayAndGetInfo(pixels, out elementSize, out bufferSize, out elementType);

      if (offset < 0)
      {
        throw new ArgumentOutOfRangeException("offset");
      }

      // We accept arrays of arbitrary value types - but not reference types.
      if (elementType == null || !elementType.IsValueType)
      {
        throw new ArgumentException("pixels");
      }

      checked
      {
        int offsetInBytes = offset * elementSize;
        if (offsetInBytes >= bufferSize)
        {
          throw new IndexOutOfRangeException();
        }

        // Get the address of the data in the array by pinning it.
        GCHandle arrayHandle = GCHandle.Alloc(pixels, GCHandleType.Pinned);
        try
        {
          // Adjust the buffer and bufferSize to account for the offset.
          IntPtr buffer = arrayHandle.AddrOfPinnedObject();
          buffer = new IntPtr(((long)buffer) + (long)offsetInBytes);
          bufferSize -= offsetInBytes;

          CopyPixels(sourceRect, buffer, bufferSize, stride);
        }
        finally
        {
          arrayHandle.Free();
        }
      }
    }

    /// <summary>
    ///     Requests pixels from this BitmapSource.
    /// </summary>
    /// <param name="sourceRect">
    ///     The rectangle of pixels to copy.
    /// </param>
    /// <param name="buffer">
    ///     The destination buffer of pixels.
    /// </param>
    /// <param name="bufferSize">
    ///     The size of the buffer, in bytes.
    /// </param>
    /// <param name="stride">
    ///     The stride of the destination buffer.
    /// </param>
    /// <remarks>
    ///     Derived classes must override CopyPixelsCommon to implement
    ///     custom logic.
    /// </remarks>
    public sealed override void CopyPixels(Int32Rect sourceRect, IntPtr buffer, int bufferSize, int stride)
    {
      // WIC would specify NULL for the source rect to indicate that the
      // entire content should be copied.  WPF turns that into an empty
      // rect, which we inflate here to be the entire bounds.
      if (sourceRect.IsEmpty)
      {
        sourceRect.Width = PixelWidth;
        sourceRect.Height = PixelHeight;
      }

      if (sourceRect.X < 0)
      {
        throw new ArgumentOutOfRangeException("sourceRect");
      }

      if (sourceRect.Width < 0)
      {
        throw new ArgumentOutOfRangeException("sourceRect");
      }

      if ((sourceRect.X + sourceRect.Width) > PixelWidth)
      {
        throw new ArgumentOutOfRangeException("sourceRect");
      }

      if (sourceRect.Y < 0)
      {
        throw new ArgumentOutOfRangeException("sourceRect");
      }

      if (sourceRect.Height < 0)
      {
        throw new ArgumentOutOfRangeException("sourceRect");
      }

      if ((sourceRect.Y + sourceRect.Height) > PixelHeight)
      {
        throw new ArgumentOutOfRangeException("sourceRect");
      }

      if (buffer == IntPtr.Zero)
      {
        throw new ArgumentNullException("buffer");
      }

      checked
      {
        if (stride < 1)
        {
          throw new ArgumentOutOfRangeException("stride");
        }

        uint minStrideInBits = (uint)(sourceRect.Width * Format.BitsPerPixel);
        uint minStrideInBytes = ((minStrideInBits + 7) / 8);
        if (stride < minStrideInBytes)
        {
          throw new ArgumentOutOfRangeException("stride");
        }

        if (bufferSize < 1)
        {
          throw new ArgumentOutOfRangeException("bufferSize");
        }

        uint minBufferSize = (uint)((sourceRect.Height - 1) * stride) + minStrideInBytes;
        if (bufferSize < minBufferSize)
        {
          throw new ArgumentOutOfRangeException("bufferSize");
        }
      }

      CopyPixelsCore(sourceRect, stride, bufferSize, buffer);
    }





    protected BitmapSource _cache = null;

    /// <summary>
    ///     Requests pixels from this BitmapSource.
    /// </summary>
    /// <param name="rc">
    ///     The caller can restrict the operation to a rectangle of
    ///     interest (ROI) using this parameter. The ROI sub-rectangle
    ///     must be fully contained in the bounds of the bitmap.
    ///     Specifying a null ROI implies that the whole bitmap should be
    ///     returned. Careful use of the ROI can be a significant
    ///     performance optimization when the pixel-production algorithm
    ///     is expensive - e.g. JPEG decoding. 
    /// </param>
    /// <param name="stride">
    ///     Defines the count of bytes between two vertically adjacent
    ///     pixels in the output buffer. 
    /// </param>
    /// <param name="bufferSize">
    ///     The size
    /// </param>
    /// <param name="buffer">
    ///     The caller controls the memory management and must provide an
    ///     output buffer of sufficient size to complete the call based on
    ///     the width, height and pixel format of the bitmap and the
    ///     sub-rectangle provided to the copy method. 
    /// </param>
    /// <remarks>
    ///     This is the main image processing routine. It instructs the
    ///     BitmapSource instance to produce pixels according to its
    ///     algorithm - this may involve decoding a portion of a JPEG
    ///     stored on disk, copying a block of memory, or even
    ///     analytically computing a complex gradient. The algorithm is
    ///     completely dependent on the implementation.
    ///     
    ///     Implementation of this method must only write to the first
    ///     (rc.Width*PixelFormat.BitsPerPixel+7)/8 bytes of each line of
    ///     the output buffer (in this case, a line is a consecutive string
    ///     of "stride" bytes).
    /// </remarks>
    protected virtual void CopyPixelsCore(Int32Rect sourceRect, int stride, int bufferSize, IntPtr buffer)
    {
      if (_cache == null)
        _cache = loadAllData();

      _cache.CopyPixels(sourceRect, buffer, bufferSize, stride);

      // if image cache is disabled then free loaded image here
      if (_cacheOption == BitmapCacheOption.None)
        _cache = null;
    }





    /// <summary>
    ///     Get the size of the specified array and of the elements in it.
    /// </summary>
    /// <param name="pixels">
    ///     The array to get info about.
    /// </param>
    /// <param name="elementSize">
    ///     On output, will contain the size of the elements in the array.
    /// </param>
    /// <param name="sourceBufferSize">
    ///     On output, will contain the size of the array.
    /// </param>
    /// <param name="elementType">
    ///     On output, will contain the type of the elements in the array.
    /// </param>
    private void ValidateArrayAndGetInfo(Array pixels,
                                         out int elementSize,
                                         out int sourceBufferSize,
                                         out Type elementType)
    {
      //
      // Assure that a valid pixels Array was provided.
      //
      if (pixels == null)
      {
        throw new ArgumentNullException("pixels");
      }

      if (pixels.Rank == 1)
      {
        if (pixels.GetLength(0) <= 0)
        {
          throw new ArgumentException("pixels");
        }
        else
        {
          checked
          {
            object exemplar = pixels.GetValue(0);
            elementSize = Marshal.SizeOf(exemplar);
            sourceBufferSize = pixels.GetLength(0) * elementSize;
            elementType = exemplar.GetType();
          }
        }

      }
      else if (pixels.Rank == 2)
      {
        if (pixels.GetLength(0) <= 0 || pixels.GetLength(1) <= 0)
        {
          throw new ArgumentException("pixels");
        }
        else
        {
          checked
          {
            object exemplar = pixels.GetValue(0, 0);
            elementSize = Marshal.SizeOf(exemplar);
            sourceBufferSize = pixels.GetLength(0) * pixels.GetLength(1) * elementSize;
            elementType = exemplar.GetType();
          }
        }
      }
      else
      {
        throw new ArgumentException("pixels");
      }
    }


    #endregion BitmapSource CopyPixels


    #region BitmapSource Download



    /// <summary>
    ///     Whether or not the BitmapSource is downloading content.
    /// </summary>
    /// <remarks>
    ///     This is not applicable to all CitBitmap classes, so
    ///     the base implementation return false.
    /// </remarks>
    public override bool IsDownloading
    {
      get
      {
        return false;
      }
    }

    /// <summary>
    ///     Raised when the downloading of content is completed.
    /// </summary>
    /// <remarks>
    ///     This is not applicable to all CitBitmap classes, so
    ///     the base implementation does nothing.
    /// </remarks>
    public override event EventHandler DownloadCompleted
    {
      add
      {
      }

      remove
      {
      }
    }

    /// <summary>
    ///     Raised when the downloading of content has progressed.
    /// </summary>
    /// <remarks>
    ///     This is not applicable to all CitBitmap classes, so
    ///     the base implementation does nothing.
    /// </remarks>
    public override event EventHandler<DownloadProgressEventArgs> DownloadProgress
    {
      add
      {
      }

      remove
      {
      }
    }

    /// <summary>
    ///     Raised when the downloading of content has failed.
    /// </summary>
    /// <remarks>
    ///     This is not applicable to all CitBitmap classes, so
    ///     the base implementation throws.
    /// </remarks>
    public override event EventHandler<ExceptionEventArgs> DownloadFailed
    {
      add
      {
      }

      remove
      {
      }
    }
    #endregion BitmapSource Download

    #region BitmapSource Decode

    /// <summary>
    ///     Raised when the downloading of content has progressed.
    /// </summary>
    /// <remarks>
    ///     This is not applicable to all CitBitmap classes, so
    ///     the base implementation does nothing.
    /// </remarks>
    public override event EventHandler<ExceptionEventArgs> DecodeFailed
    {
      add
      {
      }

      remove
      {
      }
    }

    #endregion BitmapSource Decode


    #region create

    //protected static BinaryReader _citInitReader;

    
    public static CitBitmap Create(Stream bitmapStream)
    {
      return Create(bitmapStream, BitmapCreateOptions.None, BitmapCacheOption.Default);
    }




    public static CitBitmap Create(Stream bitmapStream, BitmapCreateOptions createOptions, BitmapCacheOption cacheOption)
    {
      //FileStream _citStream = new FileStream(fileName, FileMode.Open, FileAccess.Read);

      BinaryReader citReader = new BinaryReader(bitmapStream);
      citReader.BaseStream.Seek(0, SeekOrigin.Begin);

      //try read magic numbers...
      readHeaderMagicNumber(citReader);


      // try read standard headers
      // anyway only h1 is filled for cit
      Header1 h1 = readHeader1(citReader);
      Header2 h2 = readHeader2(citReader);


      // cit must not have any other code here
      if (h1.DataTypeCode != DataCodeType.CCITTGroup4)
        throw new ArgumentException("not a cit file, must be CCITT 4 DataTypeCode");

      //int PixelWidth = (int)h1.PixelsPerLine;
      //int PixelHeight = (int)h1.NumberOfLines;


      //cit is single frame image...
      //prepareFrame();

      return new CitBitmap(bitmapStream, createOptions, cacheOption);

    }



    protected static ushort readHeaderMagicNumber(BinaryReader citReader)
    {

      ushort magic;

      //ushort h = _citReader.ReadUInt16();
      //byte igds_element_type = _citReader.ReadByte();
      magic = citReader.ReadUInt16();
      //result.IgdsMagic = _citReader.ReadUInt16();

      //if(igds_element_type != 9)
      if (magic != 0x0908)
        throw new ArgumentException("not a ingr file");


      ////byte igds_major = _citReader.ReadByte();
      //result.DimensionAndVersion = _citReader.ReadByte();

      //// only version 8 is supported
      //if ((result.DimensionAndVersion & 63) != 8)
      //  throw new ArgumentException("unsupported version " + (result.DimensionAndVersion & 63).ToString());

      //// must be 2d or 3d
      //if ((result.DimensionAndVersion & 192) != 0 && (result.DimensionAndVersion & 192) != 192)
      //  throw new ArgumentException("not a ingr file");

      //// cit must be 2d
      //if ((result.DimensionAndVersion & 192) != 0)
      //  throw new ArgumentException("not a cit file");

      return magic;

    }



    protected BitmapSource loadAllData()
    {
      // read all ccitt encoded data
      // seek after header 
      // to 1024 for normal cit
      _citReader.BaseStream.Seek(1024, SeekOrigin.Begin);
      byte[] allData = _citReader.ReadBytes((int)(_citReader.BaseStream.Length - _citReader.BaseStream.Position));

      // call decompresssion
      CompressionCCITT cp = new CompressionCCITT();
      byte[] outputB = cp.DecompressBlock(PixelWidth, PixelHeight, allData, _decodeAs8bpp);

      // prepare data
      //BitmapSource data;

      // if oputput tyoe is indexed8bpp,  prepare color table
      if (_decodeAs8bpp)
      {
        List<Color> tmpColorList = new List<Color>();
        tmpColorList.Add(Color.FromArgb(0, 0, 0, 0));
        for (int i = 1; i < 255; i++)
          tmpColorList.Add(Color.FromArgb(255, 128, 128, 128));

        tmpColorList.Add(Color.FromArgb(255, 255, 255, 255));

        return BitmapSource.Create(PixelWidth, PixelHeight, 600, 600, PixelFormats.Indexed8, new BitmapPalette(tmpColorList), outputB, Compression.getStrideLenght(PixelWidth, 8));
      }
      else
        return BitmapSource.Create(PixelWidth, PixelHeight, 600, 600, PixelFormats.BlackWhite, null, outputB, Compression.getStrideLenght(PixelWidth, 1));


      //FileStream stream = new FileStream(@"c:\temp\new.tif", FileMode.Create);
      //TiffBitmapEncoder encoder = new TiffBitmapEncoder();
      //encoder.Compression = TiffCompressOption.Ccitt4;
      //encoder.Frames.Add(BitmapFrame.Create(data));
      //encoder.Save(stream);

    }





    protected static Header1 readHeader1(BinaryReader citReader)
    {

      Header1 h1 = new Header1()
      {
        HeaderType = 0x0809,
        WordsToFollow = citReader.ReadUInt16(),
        DataTypeCode = (DataCodeType)citReader.ReadUInt16(),
        ApplicationType = citReader.ReadUInt16(),
        XViewOrigin = citReader.ReadDouble(),
        YViewOrigin = citReader.ReadDouble(),
        ZViewOrigin = citReader.ReadDouble(),
        XViewExtent = citReader.ReadDouble(),
        YViewExtent = citReader.ReadDouble(),
        ZViewExtent = citReader.ReadDouble(),
        TransformationMatrix = readTransformMatrix(citReader),
        PixelsPerLine = citReader.ReadUInt32(),
        NumberOfLines = citReader.ReadUInt32(),
        DeviceResolution = citReader.ReadInt16(),
        ScanlineOrientation = citReader.ReadByte(),
        ScannableFlag = (ScannableFlagType)citReader.ReadByte(),
        RotationAngle = citReader.ReadDouble(),
        SkewAngle = citReader.ReadDouble(),
        DataTypeModifier = citReader.ReadUInt16(),
        DesignFileName = citReader.ReadChars(66),
        DataBaseFileName = citReader.ReadChars(66),
        ParentGridFileName = citReader.ReadChars(66),
        FileDescription = citReader.ReadChars(80)
      };

      //skip min, max
      citReader.ReadBytes(16);
      //Minimum = readMinMaxType(),
      //Maximum = readMinMaxType(),
      h1.Reserved = citReader.ReadChars(3);
      h1.GridFileVersion = citReader.ReadByte();


      return h1;

    }





    protected static Header2 readHeader2(BinaryReader citReader)
    {

      Header2 result = new Header2()
      {
        Gain = citReader.ReadByte(),
        OffsetThreshold = citReader.ReadByte(),
        ViewScreen1 = citReader.ReadByte(),
        ViewScreen2 = citReader.ReadByte(),
        ViewNumber = citReader.ReadByte(),
        Reserved1 = citReader.ReadByte(),
        Reserved2 = citReader.ReadUInt16(),
        AspectRatio = citReader.ReadDouble(),
        NextImageOffset = citReader.ReadUInt32(),
        ColorTableType = citReader.ReadUInt16(),
        Reserved3 = citReader.ReadUInt16(),
        ColorTableEntries = citReader.ReadUInt32(),
        StartOfAppPacket = citReader.ReadUInt32(),
        LenOfDataPackets = citReader.ReadUInt32()
        //Reserved
        //ApplicationData
      };

      //skip reserved and app data
      citReader.ReadBytes(476);

      return result;
    }






    protected static double[] readTransformMatrix(BinaryReader citReader)
    {
      double[] result = new double[16];
      for (int i = 0; i < 16; i++)
        result[i] = citReader.ReadDouble();

      return result;
    }

    #endregion create


    #region cit specific


    public double[] TransformMatrix
    {
      get { return h1.TransformationMatrix; }
    }


    #endregion cit specific


  }
}
